/*
 *
 * Copyright (C) 2012 Walter Lütgenau
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */
package offset.nodes.client.dialog.navigation.view;

import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.ImageIcon;
import javax.swing.JApplet;
import javax.swing.SwingUtilities;
import netscape.javascript.JSObject;
import offset.nodes.client.chooser.model.GetNodeTypeIcons;
import offset.nodes.client.chooser.model.GetPropertyTypeIcons;
import offset.nodes.client.chooser.model.TypeIconProvider;
import offset.nodes.client.dialog.navigation.model.NavigationConfig;
import offset.nodes.client.dialog.navigation.model.NavigationModel;
import offset.nodes.client.dialog.navigation.model.RepositoryAware;
import offset.nodes.client.dialog.navigation.model.ServiceClient;
import offset.nodes.client.model.ServerModel;
import offset.nodes.client.dialog.navigation.model.ComponentDialogEnvironment;
import offset.nodes.client.model.CollectionRequest;
import offset.nodes.client.model.CollectionResponse;
import offset.nodes.client.model.NodeTypeIcon;
import offset.nodes.client.model.PropertyTypeIcon;
import offset.nodes.client.model.ServerResponse;
import offset.nodes.client.view.ConfiguredApplet;

/**
 *
 * @author Walter Lütgenau
 */
public class NavigationApplet extends ConfiguredApplet {

    ServerModel server = null;
    protected NavigationPanel navigationPanel = null;
    String repository = null;
    public static final String PAR_SERVICE = "service";
    public static final String PAR_REPOSITORY = "repository";
    protected HashMap<String, NavigationModel> models = new HashMap<String, NavigationModel>();

    public NavigationApplet() {
    }

    @Override
    public void initUI() {
        try {
            String url = getParameter(PAR_SERVICE);
            repository = getParameter(PAR_REPOSITORY);

            server = new ServerModel(new URL(url));

            // node type icons
            initTypeIcons(server);

            // add panels and models
            navigationPanel = new NavigationPanel();

            // add cards
            Collection<NavigationModel> navigationCards = ((NavigationConfig) getApplicationContext().getBean(NavigationConfig.BEAN_NAVIGATION_CONFIG)).getCards();
            NavigationModel selectedCard = null;
            for (NavigationModel navigationCard : navigationCards) {
                if (selectedCard == null) {
                    selectedCard = navigationCard;
                }

                navigationCard.setApplet(this);
                if (navigationCard instanceof ServiceClient) {
                    ((ServiceClient) navigationCard).setServer(server);
                }
                if (navigationCard instanceof RepositoryAware) {
                    ((RepositoryAware) navigationCard).setRepository(repository);
                }

                addCard(navigationCard);
            }

            navigationPanel.showCard(selectedCard.getPanel().getCardName());

            add(navigationPanel);
            
            update();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Show the requested card
     *
     * @param cardName the name of the card
     */
    public void show(String cardName) {
        ShowCardAction showCardAction = new ShowCardAction(cardName);
        AccessController.doPrivileged(showCardAction);
    }

    /**
     * Show another navigation card.
     */
    class ShowCardAction implements PrivilegedAction<Void> {
        String cardName;
        
        public ShowCardAction(String cardName) {
            this.cardName = cardName;
        }

        /**
         * Show the requested card
         *
         * @param cardName the name of the card
         */
        @Override
        public Void run() {
            navigationPanel.showCard(cardName);
            if (!models.get(cardName).isValid()) {
                updateCard();
            }
            
            return null;
        }
    }

    /**
     * Content has been loaded, update panel.
     */
    public void update() {
        UpdateAction update = new UpdateAction();
        AccessController.doPrivileged(update);
    }
    
    /**
     * Update the panel.
     */
    class UpdateAction implements PrivilegedAction<Void> {
        @Override
        public Void run() {
            try {
                SwingUtilities.invokeLater(new Runnable() {
                    public void run() {
                        try {
                            // update only the current model/panel
                            NavigationModel current = models.get(navigationPanel.getCurrentCard());
                            current.update();

                            // invalidate all models
                            for (NavigationModel model : models.values()) {
                                if (model != current) {
                                    model.invalidate();
                                }
                            }
                        } catch (Exception ex) {
                            Logger.getLogger(NavigationApplet.class.getName()).log(Level.SEVERE, null, ex);
                        }
                    }
                });
            } catch (Exception e) {
                e.printStackTrace();
            }
            
            return null;
        }
    }
    
   
    /**
     * Start an applet in the context of the navigation applet.
     *
     * This speeds up applet creation, as no new process must be created and expensive applet
     * initialization can be saved.
     *
     * @param parameters the applet parameters as a string of key value pairs, separated by
     * ComponentDialogEnvironment.PARAMETER_SEPARATOR.
     */
    public void startApplet(String parameters) {
        StartAppletAction startApplet = new StartAppletAction(this, parameters, null, null);
        AccessController.doPrivileged(startApplet);
    }
    
    /**
     * Start an applet and pass the originating node.
     * 
     * @param callingNode the node, which is the target of the user action
     * @param parameters the applet parameters as a string of key value pairs, separated by
     * ComponentDialogEnvironment.PARAMETER_SEPARATOR.
     */
     public void startElementApplet(JSObject callingNode, JSObject event, String parameters) {
        StartAppletAction startApplet = new StartAppletAction(this, parameters, callingNode, event);
        AccessController.doPrivileged(startApplet);
    }
    
    /**
     * Start the applet.
     */
    class StartAppletAction implements PrivilegedAction<Void> {
        JApplet base;
        String parameters;
        JSObject callingNode;
        JSObject event;

        public StartAppletAction(JApplet base, String parameters, JSObject callingNode, JSObject event) {
            this.base = base;
            this.parameters = parameters;
            this.callingNode = callingNode;
            this.event = event;
        }

        @Override
        public Void run() {
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    ComponentDialogEnvironment dialogEnvironment = new ComponentDialogEnvironment(base, parameters, callingNode, event);
                    dialogEnvironment.init();
                }
            });
            return null;
        }
    }

    /**
     * Add a card with model and panel.
     *
     * @param model the model to add
     */
    protected void addCard(NavigationModel model) throws Exception {
        model.initPanel();
        models.put(model.getPanel().getCardName(), model);
        navigationPanel.addCard(model.getPanel());
    }

    /**
     * Content has been loaded, update panel.
     */
    protected void updateCard() {
        try {
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    try {
                        models.get(navigationPanel.getCurrentCard()).update();
                    } catch (Exception ex) {
                        Logger.getLogger(NavigationApplet.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
            });

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Init the node type icons from the server configuration
     *
     * @param server the server
     * @throws Exception
     */
    protected void initTypeIcons(ServerModel server) throws Exception {
        CollectionRequest request = new CollectionRequest();
        request.addRequest(new GetNodeTypeIcons.Request());
        request.addRequest(new GetPropertyTypeIcons.Request());

        CollectionResponse collectionResponse = (CollectionResponse) server.sendRequest(request);
        Iterator<ServerResponse> responses = collectionResponse.getServerResponses();

        if (responses.hasNext()) {
            GetNodeTypeIcons.Response response = (GetNodeTypeIcons.Response) responses.next();

            for (NodeTypeIcon icon : response.getIcons()) {
                TypeIconProvider.getInstance().addNodeTypeIcon(icon.getNodeType(), new ImageIcon(icon.getIcon()));
            }
        }

        if (responses.hasNext()) {
            GetPropertyTypeIcons.Response response = (GetPropertyTypeIcons.Response) responses.next();

            for (PropertyTypeIcon icon : response.getIcons()) {
                TypeIconProvider.getInstance().addPropertyTypeIcon(icon.getPropertyType(), new ImageIcon(icon.getIcon()));
            }
        }
    }

    public ServerModel getServer() {
        return server;
    }
}
